#include "log_hub.h"

#include "logger.h"
#include "formater_pattern.h"
#include "periodic_worker.h"

#if AFCORE_PLATFORM == AFCORE_PLATFORM_WIN
#include "appender_console_color_wincolor.h"
#else
#include "appender_console_color_ansi.h"
#endif

namespace afcore {
namespace log {

CLogHub& CLogHub::Instance() {
  static CLogHub s_instance;
  return s_instance;
}

void CLogHub::RegisterLogger(RLoggerSptr new_logger) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  DoRegisterLogger(std::move(new_logger));
}

void CLogHub::InitializeLogger(RLoggerSptr new_logger) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);

  new_logger->SetFormatter(formatter_->Clone());

  if (err_handler_) {
    new_logger->SetCustomErrHandler(err_handler_);
  }

  new_logger->SetLogLevel(levels_.GetLevelByLoggerName(new_logger->GetName()));
  new_logger->SetFlushLevel(flush_level_);

  if (backtrace_n_messages_ > 0) {
    new_logger->EnableBacktrace(backtrace_n_messages_);
  }

  if (automatic_registration_) {
    DoRegisterLogger(std::move(new_logger));
  }
}

CLogger *CLogHub::GetDefaultRaw() {
  return default_logger_.get();
}

RLoggerSptr CLogHub::GetLoggerByName(const std::string& logger_name) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  auto found_iter = loggers_.find(logger_name);
  return loggers_.end() == found_iter ? nullptr : found_iter->second;
}

RLoggerSptr CLogHub::GetDefaultLogger() {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  return default_logger_;
}

void CLogHub::SetDefaultLogger(RLoggerSptr new_default_logger) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  if (nullptr != default_logger_) {
    loggers_.erase(default_logger_->GetName());
  }
  if (nullptr != new_default_logger) {
    loggers_[new_default_logger->GetName()] = new_default_logger;
  }
  default_logger_ = std::move(new_default_logger);
}

void CLogHub::SetThreadPool(RAsyncThreadPoolSptr tp) {
  std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
  tp_ = std::move(tp);
}

RAsyncThreadPoolSptr CLogHub::GetThreadPool() {
  std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
  return tp_;
}

void CLogHub::SetFormatter(RFormatterUptr formatter) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  formatter_ = std::move(formatter);
  for (auto& iter : loggers_) {
    iter.second->SetFormatter(formatter_->Clone());
  }
}

void CLogHub::EnableBacktrace(size_t n_messages) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  backtrace_n_messages_ = n_messages;
  for (auto& iter : loggers_) {
    iter.second->EnableBacktrace(n_messages);
  }
}

void CLogHub::DisableBacktrace() {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  for (auto& iter : loggers_) {
    iter.second->DisableBacktrace();
  }
}

void CLogHub::SetLogLevel(ELogLevel log_level) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  for (auto& iter : loggers_) {
    iter.second->SetLogLevel(log_level);
  }
  levels_.SetDefaultLevel(log_level);
}

void CLogHub::SetFlushLevel(ELogLevel log_level) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  for (auto& iter : loggers_) {
    iter.second->SetFlushLevel(log_level);
  }
  flush_level_ = log_level;
}

void CLogHub::FlushEvery(RSeconds interval) {
  std::lock_guard<std::mutex> lock(flush_mutex_);
  auto callback = [this]() {
    this->FlushAll();
  };

  periodic_flusher_ = std::make_unique<CPeriodicWorker>(callback, interval);
}

void CLogHub::SetErrorHander(RErrHanlder handler) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  for (auto& iter : loggers_) {
    iter.second->SetCustomErrHandler(handler);
  }
  err_handler_ = handler;
}

void CLogHub::ApplyAll(const std::function<void(const RLoggerSptr)>& fun) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  for (auto& iter : loggers_) {
    fun(iter.second);
  }
}

void CLogHub::FlushAll() {
  std::lock_guard<std::mutex> lock(flush_mutex_);
  for (auto& iter : loggers_) {
    iter.second->Flush();
  }
}

void CLogHub::Drop(const std::string& logger_name) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  loggers_.erase(logger_name);
  if (default_logger_ && default_logger_->GetName() == logger_name) {
    default_logger_.reset();
  }
}

void CLogHub::DropAll() {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  loggers_.clear();
  default_logger_.reset();
}

void CLogHub::Shutdown() {
  {
    std::lock_guard<std::mutex> lock(flush_mutex_);
    periodic_flusher_.reset();
  }
  DropAll();
  {
    std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
    tp_.reset();
  }
}

std::recursive_mutex& CLogHub::GetTpMutex() {
  return tp_mutex_;
}

void CLogHub::SetAutomaticRegistration(bool automatic_registration) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  automatic_registration_ = automatic_registration;
}

void CLogHub::UpdateLevels(CHelperLogLevel levels) {
  std::lock_guard<std::mutex> lock(logger_map_mutex_);
  levels_ = std::move(levels);
  for (auto& iter : loggers_) {
    iter.second->SetLogLevel(levels_.GetLevelByLoggerName(iter.second->GetName()));
  }
}

CLogHub::CLogHub()
  : formatter_(new CFormaterPattern()) {
#if AFCORE_PLATFORM == AFCORE_PLATFORM_WIN
  auto color_appender = std::make_shared<RAppenderWincolorStdout_MT>();
#else
  auto color_appender = std::make_shared<RAppenderAnsicolorStdout_MT>();
#endif
  const char* default_logger_name = "";
  default_logger_ = std::make_shared<CLogger>(default_logger_name, std::move(color_appender));
  loggers_[default_logger_name] = default_logger_;
}

void CLogHub::ThrowIfExists(const std::string& logger_name) {
  if (loggers_.end() != loggers_.find(logger_name)) {
    ThrowCLogeException("logger name:'" + logger_name + "' already exists");
  }
}

void CLogHub::DoRegisterLogger(RLoggerSptr new_logger) {
  auto logger_name = new_logger->GetName();
  ThrowIfExists(logger_name);
  loggers_[logger_name] = std::move(new_logger);
}

} // !namespace log
} // !namespace afcore